package logic;

import interfaze.SpaceInvadersGamingPanel;
import interfaze.SpaceInvadersHiScorePanel;

import java.util.ArrayList;

import constants.GameTables;

import entities.Bouncer;
import entities.Defender;
import entities.DefenderShot;
import entities.Enemy;
import entities.EnemyShot;
import entities.Fighter;
import entities.MovableEntity;
import entities.PowerUp;


public class EarthDefenderGameThread extends Thread {
	ArrayList<ArrayList<Enemy>> enemyLists;
	ArrayList<EnemyShot> enemyShotList=new ArrayList<EnemyShot>();;
	ArrayList<DefenderShot> defenderShotList=new ArrayList<DefenderShot>();
	ArrayList<PowerUp> powerUpList=new ArrayList<PowerUp>();
	Defender d; 
	SpaceInvadersGamingPanel p;
	public boolean ThreadIsAlive=true;
	public boolean defShot=false;
	long uDisparoJugador=System.currentTimeMillis();
	public int prob;
	long vuelta=0;
	long uDisparoEnemigo=System.currentTimeMillis();
	long uDisparoBouncer=System.currentTimeMillis();
	ArrayList<MovableEntity> rem;
	ArrayList<Enemy> enemyList;
	int enemyShotSpeed;
	int lives;
	long score=0;
	int level=1;
	EarthDefenderLevelFiller lf;
	
	/**
	 * this constructor gives the attributes the necessary values for the thread to function thus enabling gameplay
	 * @param lists the lists of enemies to include
	 * @param d the defender (player)
	 * @param p the panel in which all the items are going to be represented
	 * @param enemyShotSpeed the vertical speed the enemies' shots should have
	 */
	public EarthDefenderGameThread(ArrayList<ArrayList<Enemy>> lists, Defender d, SpaceInvadersGamingPanel p, int enemyShotSpeed, int lives,EarthDefenderLevelFiller lf){
		this.lives=lives;
		this.enemyShotSpeed=enemyShotSpeed;
		this.enemyShotSpeed=100;
		enemyLists=lists;
		this.d=d;
		this.p=p;
		d.setCanMove(true);
		enemyList=new ArrayList<Enemy>();
		for(ArrayList<Enemy> l:enemyLists){
			for(Enemy e:l){
				enemyList.add(e);
			}
		}
		this.lf=lf;
		
	}
	public void run(){
		boolean visibleEnemies=false;
		ArrayList<Fighter> rob;
		boolean PlayerIsAlive=true;
		/**
		 * this while will function as long as it's desired using the ThreadIsAlive boolean value, while doing so it will handle all gameplay features within the game
		 * additionally, the PlayerIsAlive value will be false when the number of hits the defender has received is higher than the lives
		 */
		while(ThreadIsAlive && PlayerIsAlive){
			visibleEnemies=false;
			/**
			 * rem is an ArrayList used to store and then remove the objects which are no longer useful and should not remain in memory, thus easing the load on memory and processor
			 * rob is an ArrayList used to store the Fighters going off bounds, its exact purpose is detailed below
			 */
			rem=new ArrayList<MovableEntity>();
			rob=new ArrayList<Fighter>();
			//System.out.println("vuelta del thread principal: "+ vuelta);
			//vuelta++;
			/**
			 * this for each handles the movement and shooting of all enemies on screen
			 */
			for (Enemy e: enemyList){
				if(e.isVisible()){
					e.collidesWithDefender(d);
					if (e instanceof Fighter){
					if(e.update2()){
						rob.add((Fighter)e);
					}
					if(((Fighter) e).dispara(enemyShotSpeed, prob, uDisparoEnemigo,enemyShotList)){
						uDisparoEnemigo=System.currentTimeMillis();
					}
					}
					if(e instanceof Bouncer){
					if(e.update2()){
						e.setDX(-e.getDX());
						if(e.getX()<0){
							e.setX(e.getX()+1);
						}else{
							e.setX(e.getX()-1);
						}
					}
					if(System.currentTimeMillis()-uDisparoBouncer>5000){
						((Bouncer) e).dispara( enemyShotSpeed, enemyShotList);
						uDisparoBouncer=System.currentTimeMillis();
					}
					}
				}else{
					rem.add(e);
				}
				visibleEnemies=visibleEnemies||e.isVisible();
			}
			/*for(ArrayList<Enemy> l : enemyLists){
				int enemigos=0;
				for(Enemy e:l){
					if(e.isVisible()){
					enemigos++;
					if(e.update2()&& e instanceof Fighter){
						rob.add((Fighter)e);
					}
					if(e.update2()&& e instanceof Bouncer){
						e.setDX(-e.getDX());
						if(e.getX()<0){
							e.setX(e.getX()+1);
						}else{
							e.setX(e.getX()-1);
						}
					}
					if(e instanceof Fighter){
						if(((Fighter) e).dispara(60, prob, uDisparoEnemigo,enemyShotList)){
							uDisparoEnemigo=System.currentTimeMillis();
						}
					}
					if(e instanceof Bouncer && System.currentTimeMillis()-uDisparoBouncer>5000){
						((Bouncer) e).dispara( 60, enemyShotList);
						uDisparoBouncer=System.currentTimeMillis();
					}
					System.out.println("("+e.getX()+", "+e.getY()+")");
					visibleEnemies=visibleEnemies||e.isVisible();
				}
				System.out.println("hay "+enemigos+" enemigos");
				}
			}*/
			/**
			 * this for each handles the movement and imapct control for enemy shots
			 */
			for(EnemyShot e:enemyShotList){
				if(e.isVisible()){
				e.impacts(d);
				if(e.update2()){
					e.bounceOff();
					/*e.setDX(-e.getDX());
					if(e.getX()<0){
						e.setX(e.getX()+1);
					}else{
						e.setX(e.getX()-1);
					}*/
				}
				}else if(!e.isVisible()||!e.getBounds().intersects(this.p.window.getBounds())){
					rem.add(e);
				}
			}
			/**
			 * this for each handles the movement and impact control for the player's shots
			 */
			for(DefenderShot ds:defenderShotList){
				Enemy e=null;
				if(ds.isVisible()){
						e=ds.impacts(enemyList, powerUpList);
						ds.update2();
				}else if(!ds.isVisible()||!ds.getBounds().intersects(this.p.window.getBounds())){
					rem.add(ds);
				}
				if(e!=null){
					if(e instanceof Fighter){
						score+=100*level;
					}else{
						score+=500*level;
					}
				}
			}
			/**
			 * this for each handles the movement of power ups and controls whether the player has picked them up or not, changing its attributes accordingly
			 */
			for(PowerUp pu:powerUpList){
				if(pu.isVisible()){
				pu.pickedUp(d);
				pu.update2();
				}else if(!pu.isVisible()||!pu.getBounds().intersects(this.p.window.getBounds())){
					rem.add(pu);
				}
			}
			if(d.update2()){
				d.bounceOff();
			}
			/**
			 * defShot is activated when the player presses the spacebar, that is, when the player decides to shoot
			 * this if controls whether enough time has passed or not and turns the flag back to false to prevent the game from shooting on its own
			 */
			if(defShot){
				if((System.currentTimeMillis()-uDisparoJugador > d.tRecarga) ){
					d.shoot(defenderShotList);
					uDisparoJugador=System.currentTimeMillis();
					defShot=false;
				}else{
					defShot=false;
				}
			}
			/**
			 * due to the impossibility of handling the turning around behavior of Fighters inside their for each, this other for each stores the fighters which have gone off bounds and makes their whole row turn around
			 */
			for(Fighter f: rob){
				f.rowBounceOff(f.getX() < 0);
			}
			/**
			 * this for each handles the removal of any object that is no longer useful and is thus taking processing power and memory off the system with no particular purpose whatsoever.
			 */
			for(MovableEntity o: rem){
				if(o instanceof EnemyShot){
					enemyShotList.remove(o);
				}else if(o instanceof DefenderShot){
					defenderShotList.remove(o);
				}else if (o instanceof PowerUp){
					powerUpList.remove(o);
				}else if(o instanceof Enemy){
					enemyList.remove(o);
				}
				p.remove(o);
			}
			/**
			 * if the player dies, the scoreboard is shown and the game ends
			 */
			if(d.hits>=lives){
				PlayerIsAlive=false;
				System.out.println(p.window.getUserLogged() + " " + p.points.getPoints());
				database.Insert.insertRecord(p.window.getUserLogged(), p.points.getPoints(), GameTables.SPACEINVADERS);
				p.window.add(new SpaceInvadersHiScorePanel(p.window));
				p.setVisible(false);
				p.window.remove(p);
				p.window.validate();
			}
			/**
			 * if no enemies remain, the game goes up a level
			 */
			if(!visibleEnemies){
				levelup();
			}
			p.lives.setLives(lives-d.hits);
			p.points.setPoints(score);
			p.level.setLevel(level);
		}
	}
	/**
	 * this method increases the level by one and fills the screen once more with enemies
	 */
	public void levelup(){
		level++;
		enemyLists=new ArrayList<ArrayList<Enemy>>();
		enemyLists=lf.addEnemigos();
		enemyList=new ArrayList<Enemy>();
		for(DefenderShot d:defenderShotList){
			d.setVisible(false);
			p.remove(d);
		}
		defenderShotList=new ArrayList<DefenderShot>();
		for(EnemyShot e:enemyShotList){
			e.setVisible(false);
			p.remove(e);
		}
		enemyShotList=new ArrayList<EnemyShot>();
		for(ArrayList<Enemy> l:enemyLists){
			for(Enemy e:l){
				enemyList.add(e);
			}
		}
		p.updateUI();
	}
}
